﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Specialized;
using Odyssey.Utils;
using System.Linq;

namespace Odyssey.Controls.Primitives
{
    /// <summary>
    /// Base panel to be used as items container for all controls which implement IItemsControl.
    /// </summary>
    public abstract class OdcVirtualizingPanel : Panel, ICollectionChangedListener
    {
        public OdcVirtualizingPanel()
            : base()
        {
            this.Loaded += new RoutedEventHandler(OnLoaded);
            this.Unloaded += new RoutedEventHandler(OnUnloaded);
        }

        #region classes

        protected struct Range
        {
            public Range(int from, int to)
            {
                From = from;
                To = to;
            }
            public int From;
            public int To;

            public int Count { get { return To - From + 1; } }

            public bool Contains(int value)
            {
                return value >= From && value <= To;
            }

            public static bool operator ==(Range a, Range b)
            {
                return a.From == b.From && a.To == b.To;
            }

            public static bool operator !=(Range a, Range b)
            {
                return a.From != b.From || a.To != b.To;
            }

            public override int GetHashCode()
            {
                return From ^ To;
            }

            public override bool Equals(object obj)
            {
                if (!(obj is Range)) return false;
                return (Range)obj == this;
            }

        }

        #endregion
        #region fields

        private bool isInvalidated = true;
        protected bool IsLoaded { get; private set; }
        private IItemsControl itemsControl;


        #endregion
        #region props

        protected bool IsInvalidated
        {
            get { return isInvalidated; }
            set { isInvalidated = value; }
        }

        protected IItemsControl ItemsControl
        {
            get
            {
                if (itemsControl == null) itemsControl = this.GetVisualAncestors().First(c => c is IItemsControl) as IItemsControl;
                return itemsControl;
            }
        }

        #endregion
        #region methods

        internal protected virtual void Reset()
        {
            ScrollIntoView(0);
            Invalidate();
        }


        /// <summary>
        /// Recycle and remove items from panel which are no longer in the current range.
        /// </summary>
        /// <param name="previous">Previous range of indexes in range.</param>
        /// <param name="current">Current range of indexes in range.</param>
        protected void RecycleElementsOutOfRange(Range previous, Range current, bool removeFromChildren)
        {
            int n = Children.Count;
            if (n > 0 || (previous != current))
            {
                int from = current.From;
                int to = current.To;
                IItemsControl itemsControl = ItemsControl;
                for (int i = n - 1; i >= 0; i--)
                {
                    int index = i + previous.From;
                    if (!current.Contains(index))
                    {
                        if (removeFromChildren) Children.RemoveAt(i);
                        itemsControl.ReleaseItemContainer(index);
                    }
                }
            }
        }
        protected virtual void OnLoaded(object sender, RoutedEventArgs e)
        {
            IsLoaded = true;
            if (IsInvalidated)
            {
                InvalidateMeasure();
            }
        }

        protected virtual void OnUnloaded(object sender, RoutedEventArgs e)
        {
            IsLoaded = false;
        }

        public void Invalidate()
        {
            if (!isInvalidated)
            {
                isInvalidated = true;
                InvalidateMeasure();
            }
        }

        protected virtual void OnItemsChanged(NotifyCollectionChangedEventArgs e)
        {
        }

        void ICollectionChangedListener.OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            OnItemsChanged(e);
        }

        /// <summary>
        /// Scrolls the specified item into view if not already present.
        /// </summary>
        /// <param name="itemIndex">index of the item to scroll into view.</param>
        public abstract void ScrollIntoView(int itemIndex);

        #endregion

    }
}
